﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Xml.Linq;

public static class AsyncDownloader
{
    private static HttpWebRequest _request;
    private static string localFilePath, fileUrl;
    private static EventHandler OnEnd;
    public static StringBuilder Log = new StringBuilder();

    public static PageAsyncTask GetTask(string path, string url, EventHandler EndedEvent)
    {
        OnEnd = EndedEvent;
        localFilePath = path;
        fileUrl = url;
        Log.Append("AsyncDownloader Log:");
        if (!File.Exists(path+".stats"))
        {
            return new PageAsyncTask(
                new BeginEventHandler(BeginNewRatesRequest),
                new EndEventHandler(EndNewRatesRequest),
                new EndEventHandler(TimeoutNewRatesRequest),
                null);
        }
        else
        {
            return new PageAsyncTask(
                new BeginEventHandler(BeginRatesRequest),
                new EndEventHandler(EndRatesRequest),
                new EndEventHandler(TimeoutRatesRequest),
                null);
        }
    }

    private static IAsyncResult BeginRatesRequest(object sender, EventArgs e, AsyncCallback cb, object extraData)
    {
        _request = (HttpWebRequest)HttpWebRequest.Create(fileUrl);
        string[] stats = File.ReadAllLines(localFilePath + ".stats");
        string eTag = stats[0], ifModifiedState = stats[1];
        _request.Headers[HttpRequestHeader.IfNoneMatch] = eTag;
        _request.IfModifiedSince = DateTime.Parse(ifModifiedState);
        return _request.BeginGetResponse(cb, extraData);
    }
    private static void EndRatesRequest(IAsyncResult ar)
    {
        try
        {
            Log.Append("\nUpdating...");
            HttpWebResponse response = (HttpWebResponse)_request.EndGetResponse(ar);
            SaveSourceFile(localFilePath, response);
        }
        catch (System.Net.WebException ex)
        {
            if (ex.Response != null)
            {
                using (HttpWebResponse response = ex.Response as HttpWebResponse)
                {
                    if (response.StatusCode == HttpStatusCode.NotModified)
                        Log.Append("\nUpdate not necessary");
                    else
                        Log.Append("\n2:Unexpected status code returned: " + response.StatusCode);
                }
            }
        }
        finally
        {
            if (OnEnd != null) OnEnd(null, EventArgs.Empty);
        }
    }
    private static void TimeoutRatesRequest(IAsyncResult ar)
    {
        Log.Append("\nRequest timeout");
        if (OnEnd != null) OnEnd(null, EventArgs.Empty);
    }
    private static IAsyncResult BeginNewRatesRequest(object sender, EventArgs e, AsyncCallback cb, object extraData)
    {
        _request = (HttpWebRequest)HttpWebRequest.Create(fileUrl);
        return _request.BeginGetResponse(cb, extraData);
    }
    private static void EndNewRatesRequest(IAsyncResult ar)
    {
        try
        {
            Log.Append("\nDownloading...");
            HttpWebResponse response = (HttpWebResponse)_request.EndGetResponse(ar);
            SaveSourceFile(localFilePath, response);
        }
        catch (Exception ex)
        {
            Log.Append("\nUnexpected problems with first download:\n" + ex.Message);
        }
        finally
        {
            if (OnEnd != null) OnEnd(null, EventArgs.Empty);
        }
    }
    private static void TimeoutNewRatesRequest(IAsyncResult ar)
    {
        Log.Append("\nRequest for new file timeout");
        if (OnEnd != null) OnEnd(null, EventArgs.Empty);
    }

    // TODO: This method does not fit
    // should be moved to the caller's class
    private static void SaveSourceFile(string localFilePath, HttpWebResponse response)
    {
        // Save last date of modification or eTag
        WebHeaderCollection headers = response.Headers;
        using (Stream stream = response.GetResponseStream())
        {
            DataClassesDataContext context = new DataClassesDataContext();
            XDocument xDoc = XDocument.Load(stream);
            foreach (var result in xDoc.Descendants("pozycja"))
            {
                KursyA record = new KursyA();
                record.nazwa_waluty = result.Element("nazwa_waluty").Value;
                record.przelicznik = int.Parse(result.Element("przelicznik").Value);
                record.kod_waluty = result.Element("kod_waluty").Value;
                record.kurs_sredni = result.Element("kurs_sredni").Value;
                KursyA oldRecord = context.KursyAs.FirstOrDefault(x => x.kod_waluty == record.kod_waluty);
                if (oldRecord != null)
                {
                    if (oldRecord.kurs_sredni != record.kurs_sredni) oldRecord.kurs_sredni = record.kurs_sredni;
                    else if (oldRecord.nazwa_waluty != record.nazwa_waluty) oldRecord.nazwa_waluty = record.nazwa_waluty;
                    else if (oldRecord.przelicznik != record.przelicznik) oldRecord.przelicznik = record.przelicznik;
                    else continue;
                }
                else context.KursyAs.InsertOnSubmit(record);
            }
            context.SubmitChanges();
        }
        File.WriteAllText(localFilePath + ".stats", String.Format("{0}\n{1}",
            headers[HttpResponseHeader.ETag],
            headers[HttpResponseHeader.LastModified]));
        response.Close();
        Log.Append("\nSaved updated values.");
    }
}